package ComoViajo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;


public class Viaje {
	private ArrayList<EstacionInterface> estaciones =new ArrayList<EstacionInterface>();
	private EstacionInterface origen=null;
	private EstacionInterface destino=null;
	
	public Viaje() {}

	public Viaje(ArrayList<EstacionInterface> recorrido) {
		this.estaciones = recorrido;
	}

	public Viaje(ArrayList<EstacionInterface> recorrido,EstacionInterface origen,EstacionInterface destino) {
		this.estaciones = recorrido;
		this.destino=destino;
		this.origen=origen;
	}

	public void addEstacion(EstacionInterface e){
		this.estaciones.add(e);
	}
	
	public int getTiempo() {
		int tiempoRecorrido = 0;
		EstacionInterface estacionAnterior=null;
		
		for (EstacionInterface estacion : this.estaciones) {
			tiempoRecorrido += estacion.getTiempoDesdeEstacion(estacionAnterior);
			estacionAnterior=estacion;
		}
		
		return tiempoRecorrido;
	}
	
	public float getCosto() {
		float costoRecorrido = 0;
		EstacionInterface eAnterior = null,e=null;
		
		int estacionesVisitadas=0;
		
		Iterator<EstacionInterface> estacionItr = this.estaciones.iterator();
		
		if (estacionItr.hasNext()){
			e = estacionItr.next();
			estacionesVisitadas++;
		}
		
		while(estacionItr.hasNext()) {
			eAnterior=e;
			e = estacionItr.next();
			
			estacionesVisitadas++;
			
			if (((!e.estaEnLaMismaLineaQue(eAnterior)))||(!estacionItr.hasNext())) {
				costoRecorrido = costoRecorrido + eAnterior.getCosto(e,estacionesVisitadas);
				estacionesVisitadas =0;
			}
			
			
		}
		//costoRecorrido = costoRecorrido + e.getCosto(null,estacionesVisitadas);	
		
		return costoRecorrido;
	}
	
	public int getUso() {
		int usoTotal = 0;
		
		for (EstacionInterface e : this.estaciones) {
			usoTotal += e.getUso();
		}
		
		return usoTotal;
	}
	
	/**
	 * 
	 * @return la cantidad de combinaciones que vamos haciendo en este recorrido
	 */
	public int getCantidadDeCombinaciones() {
		int totalCombinaciones=0;
		EstacionInterface estacionPrev=null;
						
		for (EstacionInterface estacion : this.estaciones) {
			if((estacionPrev!=null)&&(!estacion.estaEnLaMismaLineaQue(estacionPrev))){
				totalCombinaciones=totalCombinaciones+1;
			}
			estacionPrev=estacion;			
		}
						
		return totalCombinaciones;
	}
	
	public ArrayList <EstacionInterface> getEstaciones(){
		return this.estaciones;	
	}
		
	
	public static ArrayList<Viaje> getViajes(EstacionInterface estacionOrigen,EstacionInterface estacionDestino) {
		ArrayList<Viaje> viajes = new ArrayList<Viaje>();
		ArrayList<EstacionInterface> recorrido = new ArrayList<EstacionInterface>();
		
		if (estacionOrigen.estaEnLaMismaLineaQue(estacionDestino)) {
			recorrido=Viaje.viajarHastaEstacionEnLaMismaLinea(estacionOrigen,estacionDestino);
			viajes.add(new Viaje(recorrido,estacionOrigen,estacionDestino)); 
		}else{
			
			ArrayList<HashMap<String, EstacionInterface>> combinaciones = estacionOrigen.getEstacionesQueCombinanCon(estacionDestino);
			
			for (HashMap<String, EstacionInterface> combinacion : combinaciones) {
				recorrido=Viaje.viajarHastaEstacionEnLaMismaLinea(estacionOrigen,combinacion.get("combinacionEstacionOrigen"));
				ArrayList<EstacionInterface> masEstaciones=Viaje.viajarHastaEstacionEnLaMismaLinea(combinacion.get("combinacionEstacionDestino"),estacionDestino);
				recorrido.addAll(masEstaciones);
				viajes.add(new Viaje(recorrido,estacionOrigen,estacionDestino));
			}
		}
		
		return viajes;
	}
	
	public static Viaje getViajePorCriterio(EstacionInterface origen, EstacionInterface destino, CriterioViajeStrategy criterio) {
		return criterio.obtener(Viaje.getViajes(origen, destino));
	}
		
	public String toString(){
		String recorridoString="";	
		EstacionInterface estacionPrev=null;
		
		for (EstacionInterface estacion : this.estaciones) {
			if(estacionPrev==null){
				recorridoString="{"+estacion.toString();
			}else if(!estacion.estaEnLaMismaLineaQue(estacionPrev)){
				recorridoString+="}{"+estacion.toString();
			}else{
				recorridoString+=","+estacion.toString();
			}
			estacionPrev=estacion;
			
		}
		
		if(estacionPrev!=null)recorridoString+="}";
		
		return recorridoString;
	}

	public static ArrayList<EstacionInterface> viajarHastaEstacionEnLaMismaLinea(EstacionInterface estacionOrigen, EstacionInterface estacionDestino){
		ArrayList<EstacionInterface> recorrido;
		EstacionInterface estacionActual;
		
		
		estacionActual=estacionOrigen;
		recorrido = new ArrayList<EstacionInterface>();
		
		while (estacionActual != null) {
			recorrido.add(estacionActual);
						
			if (estacionActual.equals(estacionDestino)) {
				return recorrido;
			}
			estacionActual=estacionActual.getEstacionSiguiente(estacionDestino);
		}	
		
		estacionActual=estacionOrigen;
		recorrido = new ArrayList<EstacionInterface>();
		
		while (estacionActual != null) {
			recorrido.add(estacionActual);
						
			if (estacionActual.equals(estacionDestino)) {
				return recorrido;
			}
			estacionActual=estacionActual.getEstacionAnterior();
		}	
				
		return new ArrayList<EstacionInterface>();
	}

	public static ArrayList<Viaje> getViajes(Direccion direccionOrigen, Direccion direccionDestino) {
		// REFACTOR. EST� ACOPLADO EL MOCK EN EL CODIGO DE VIAJE. 
		// PARA DESACOPLARLO PUEDEN USAR UN moduloExternoFactory.createInstance() y ahi aislan el codigo que puede cambiar (el mock)
		// La segunda opcion es que venga por parametro el modulo externo, por lo que la responsabilidad
		// de setearle que tipo de modulo externo queda en el que lo llame.
		// Tercera opcion, dependency injection, se crea un field con la interfaz que desean usar
		// y se lo setean por constructor o cuando configuran inicialmente el sistema.
		ModuloExternoMapaDeTransporte moduloExterno = FactoryModuloExterno.getMapaDeTransporte();
		ArrayList<EstacionInterface> estacionesCercanasAlOrigen,estacionesCercanasAlDestino;
				
		estacionesCercanasAlOrigen = moduloExterno.getTransportesCercanos(direccionOrigen);
		estacionesCercanasAlDestino = moduloExterno.getTransportesCercanos(direccionDestino);
		
		ArrayList<Viaje> viajes = new ArrayList<Viaje>();
		
		// REFACTOR. LONG METHOD. SACAR EL FOR A OTRO METODO
		for (EstacionInterface estacionOrigen : estacionesCercanasAlOrigen) {
			viajes=Viaje.getViajes(estacionOrigen, estacionesCercanasAlDestino, viajes);
		}
		
		return viajes;
	}
	
	private static ArrayList<Viaje> getViajes(EstacionInterface estacionOrigen,ArrayList<EstacionInterface> estacionesCercanasAlDestino,ArrayList<Viaje> viajes){
		ArrayList<Viaje> losViajes;
		for (EstacionInterface estacionDestino : estacionesCercanasAlDestino) {
			if((estacionOrigen instanceof Colectivo)&&(!(estacionDestino instanceof Colectivo))){
				//si el viaje es colectivo a otro medio
				//invertir el destino
				losViajes=Viaje.getViajes(estacionDestino,estacionOrigen);
				//  luego invertir el resultado
									
				Viaje.invertirEstacionesDeViajes(losViajes);
			}else{
				losViajes=Viaje.getViajes(estacionOrigen, estacionDestino);
			}
			
			viajes.addAll(losViajes);
		}
		return viajes;
	}
			
	private static void invertirEstacionesDeViajes(ArrayList<Viaje> losViajes) {
		for (Viaje v : losViajes) {
			List<EstacionInterface> list = v.estaciones;
			Collections.reverse(list);
		}
	}

	public EstacionInterface getOrigen() {
		return this.origen;
	}

	public EstacionInterface getDestino() {
		return this.destino;
	}
	
	
}